﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConsoleLib;
using ConsoleLib.Menu.TextualMenu;
using frag_boardgame;

using System.IO;

namespace GameConsole
{
    enum CommandOptins { Continue, GameOver }
    public class UI
    {
        static MainMenu mainMenu = new MainMenu();
        static HelpMenu helpMenu = new HelpMenu();
        static IGame game;
       
        public static ToShow toShow = ToShow.MainMenu;
        static String Command;
        
        static CommandOptins commantReturn = CommandOptins.Continue;

        static public void Run(IGame game)
        {
            UI.game = game;

            //initialize and draw menus
            mainMenu.createMenu();
            helpMenu.createMenu();
            mainMenu.drawMenu();
            Command = Console.ReadLine();

            //Display loop
            while (true)
            {
                bool toRead = printDisplay();
                if (toRead) Command = Console.ReadLine();
            }

        }

        static Configuraion GetConfiguration()
        {
            GameConfiguration gameConf = ConsoleLib.ConsoleUI.GetConfiguration();
            Configuraion conf = new Configuraion(gameConf);

            for (int i = 0; i < gameConf.NumberOfPlayers; i++)
                SetPlayerConfiguration(i, conf);


            return conf;



        }
        private static bool printDisplay()
        {
            switch (toShow)
            {
                case ToShow.MainMenu:
                    {
                        switch (Command)
                        {
                            case "1":
                                toShow = ToShow.Game;
                                Player.PlayersCount = 0;
                                Configuraion conf = GetConfiguration();
                                game.New(conf);
                                addTestWalls(game.Data.Board);
                                game.StartGame();
                                return false;

                            case "2":
                                ConsoleUI.WriteToConsole("Enter file name");
                                string filename = Console.ReadLine();
                                try
                                {
                                    using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
                                    {
                                        game.Load(fs);
                                    }
                                    toShow = ToShow.Game;
                                    return false;
                                }
                                catch (IOException e)
                                {
                                    ConsoleUI.WriteToConsole(e.Message);

                                    toShow = ToShow.MainMenu;
                                    break;


                                }
                                catch (ArgumentException)
                                {
                                    ConsoleUI.WriteToConsole("Illgal file name!");
                                    toShow = ToShow.MainMenu;
                                    break;

                                }





                            case "3":
                                PrintHighScores(game);

                                toShow = ToShow.MainMenu;
                                break;

                            case "4":
                                toShow = ToShow.MainMenu;
                                helpMenu.drawMenu();
                                break;

                            case "5":
                                ExitGame();
                                break;

                            default:
                                mainMenu.drawMenu();
                                return true;
                        }
                        return true;
                    }
                case ToShow.Game:
                    {
                        ConsoleUI.PrintValues(game.Data);


                        Command = Console.ReadLine();


                        bool quit = false;
                        while ((!quit) && (commantReturn == CommandOptins.Continue))
                        {
                            if (Command == "q")
                            {
                                ConsoleUI.WriteToConsole("Are you sure you want to leave the current game? (y\\n)");
                                string decision = Console.ReadLine();
                                if (decision == "y")
                                {
                                    break;
                                }

                            }

                            if (Command != "q") commantReturn = ReadCommand(Command);
                            if (commantReturn == CommandOptins.Continue)
                            {
                                ConsoleUI.PrintValues(game.Data);
                                Command = Console.ReadLine();
                            }
                        }

                        //We want to see the message:  should be Deleted in release version.
                        if (commantReturn == CommandOptins.GameOver)
                        {
                            commantReturn = CommandOptins.Continue;
                            Console.ReadKey();
                        }
                        toShow = ToShow.MainMenu;
                        mainMenu.drawMenu();
                    }
                    return true;
                default:
                    return true;

            }
        }


        private static void ExitGame()
        {
            //We want to see the message:  should be Deleted in release version.
            ConsoleUI.WriteToConsole("Press any key to leave \"FRAG\"");
            Console.ReadKey();

            Environment.Exit(1);
        }


        static CommandOptins ReadCommand(string command)
        {
            ConsoleUI.SetLastMessage(null);
            String lastMessage=null;
            string action;
            if (command != "")
                action = command.Substring(0, 1);
            else
                action = "";


            if (command.StartsWith("save"))
            {
                ConsoleUI.WriteToConsole("Enter file name");
                string filename = Console.ReadLine();
                try
                {
                    using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
                    {
                        game.Save(fs);
                    }
                }
                catch (IOException e)
                {
                    ConsoleUI.WriteToConsole(e.Message);
                }
                catch (ArgumentException)
                {
                    ConsoleUI.WriteToConsole("Illegal file name!");
                }
            }
            else if (command.StartsWith("undo"))
            {
                try
                {
                    game.Undo();
                }
                catch (InvalidOperationException)
                {
                    BadKeyMessage("Can't Undo");
                }
            }
            else if (command.StartsWith("redo"))
            {
                try
                {
                    game.Redo();
                }
                catch (InvalidOperationException)
                {
                    BadKeyMessage("Can't Redo");
                }
            }

            else if (action == "f")
            {
                game.FinishCurrentTurn();
            }

            else if (action == "h")
            {
                helpMenu.drawMenu();
                Console.ReadKey();
            }

            else
            {
                int value = -1;
                try
                {
                    value = int.Parse(command.Substring(1));
                }
                catch (ArgumentOutOfRangeException) //if the string is empty
                {
                    return BadKeyMessage("No command was enterd");
                }
                catch (OverflowException)
                {
                    switch (action)
                    {
                        case "r":
                            {
                                return BadKeyMessage("Number of steps is incorrect");
                            }
                        case "d":
                            {
                                return BadKeyMessage("Number of steps is incorrect");
                            }
                        case "u":
                            {
                                return BadKeyMessage("Number of steps is incorrect");
                            }
                        case "l":
                            {
                                return BadKeyMessage("Number of steps is incorrect");
                            }
                        case "s":
                            {
                                return BadKeyMessage("Player number is incorrect");
                            }




                        default:
                            {
                                return BadKeyMessage("Command has incorrect format");
                            }
                    }
                }
                catch (FormatException)
                {
                    return BadKeyMessage("Incorrect command format");
                }
                Direction dir;
                if (action == "r")
                {
                    dir = Direction.Right;
                    game.MovePlayer(value, dir);
                }
                else if (action == "l")
                {
                    dir = Direction.Left;
                    game.MovePlayer(value, dir);
                }
                else if (action == "d")
                {
                    dir = Direction.Down;
                    game.MovePlayer(value, dir);
                }
                else if (action == "u")
                {
                    dir = Direction.Up;
                    game.MovePlayer(value, dir);
                }
                else if (action == "s")
                {




                    ShootingValues shootingValues = game.Shoot(value);
                    ShootingOptionsEnum shootingRes = shootingValues.Options;
                    string shooter = shootingValues.ShooterName;
                    string target = shootingValues.TargetName;
                    if (shootingRes == ShootingOptionsEnum.Hit)
                    {
                        
                        lastMessage = "Player " + shooter + " hit Player " + target;
                        lastMessage += "\nPlayer " + shooter + " Rolls: " + shootingValues.ShooterDices;
                        lastMessage += "\nPlayer " + target + " Rolls: " + shootingValues.TargetDices;
                        lastMessage += "\nDamage is: " + shootingValues.ShooterDices / shootingValues.TargetDices;


                    }
                    else if (shootingRes == ShootingOptionsEnum.Miss)
                    {
                        lastMessage = "Player " + shooter + " missed Player " + target;
                    }
                    else if (shootingRes == ShootingOptionsEnum.CantShoot)
                    {
                        lastMessage = "Player " + shooter + " can't shoot Player " + target;
                    }
                    else if (shootingRes == ShootingOptionsEnum.GameOver || shootingRes == ShootingOptionsEnum.PlayerKilled)
                    {
                        lastMessage = "Player " + target + " was killed by " + shooter;
                        if (shootingRes == ShootingOptionsEnum.GameOver)
                        {
                            ConsoleUI.WriteToConsole(lastMessage);
                            ConsoleUI.WriteToConsole("Game over! " + shooter + " Wins!!");
                            return CommandOptins.GameOver;
                        }


                    }
                   
                     ConsoleUI.SetLastMessage(lastMessage);

                }

                else
                {
                    ConsoleUI.WriteToConsole("Incorrect command format");
                }

            }
            return CommandOptins.Continue;
        }



        private static CommandOptins BadKeyMessage(string s)
        {
            ConsoleUI.WriteToConsole(s);
            ConsoleUI.WriteToConsole("Press any key to continue");
            Console.ReadKey();
            return CommandOptins.Continue;
        }




   





        private static void PrintHighScores(IGame game)
        {
            ICollection<StatisticsTable.PlayerStatistics> players = game.Statistics.GetAllPlayerStatistics();

            Console.WriteLine("== High Scores ==");

            if (players.Count > 0)
            {
                int maxPlayerNameLen = 0;
                foreach (StatisticsTable.PlayerStatistics pls in players)
                {
                    if (pls.Name.Length > maxPlayerNameLen)
                    {
                        maxPlayerNameLen = pls.Name.Length;
                    }
                }

                Console.WriteLine("{0}  Times Won", "Name".PadRight(maxPlayerNameLen, ' '));
                Console.WriteLine("{0}  =========", "====".PadRight(maxPlayerNameLen, '='));

                foreach (StatisticsTable.PlayerStatistics pls in players)
                {
                    Console.WriteLine("{0}  {1}", pls.Name.PadRight(maxPlayerNameLen), pls.TimesWon);
                }
            }
            else
            {
                Console.WriteLine("No statistics collected yet.");
            }
        }

        private static void SetPlayerConfiguration(int index, Configuraion conf)
        {
            int row = -1;
            int col = -1;
            string name = "Player " + (index + 1).ToString(); ;
            ConsoleUI.WriteToConsole("Enter Name for " + name);
            string read = Console.ReadLine();
            if (read != "")
                name = read;


            ConsoleUI.WriteToConsole("Enter row and column for player " + name + " [Default is a randomized values]:");
            Console.Write("     Row: ");
            read = Console.ReadLine();
            if (read != "")
            {
                int tmp;
                if (int.TryParse(read,out tmp)) //continue to col only if row is good
                {
                    row = tmp;
                    Console.Write("\n");
                    Console.Write("     Col: ");
                    read = Console.ReadLine();
                    if (read != "")
                        if (int.TryParse(read, out tmp))
                        {
                            col = tmp;

                        }

                }

            }

            PlayerConfiguration playerConf = new PlayerConfiguration(row, col, name);

            ConsoleUI.WriteToConsole("Enter stats (Health, Speed and Accuracy) separated by commas for " + name + "\nStats must be between 1 and 4, and their sum mustn't exceed 7");
            read = Console.ReadLine();
            string[] s = read.Split(',');
            bool success = true;
            if (s.Length < 3) {
                success = false;
            } else {
                int health=0, speed=0, accuracy=0;
                success = int.TryParse(s[0], out health);
                success = success && int.TryParse(s[1], out speed);
                success = success && int.TryParse(s[2], out accuracy);

                if (success)
                {
                    try
                    {
                        playerConf.SetStats(health, speed, accuracy);
                    } catch (ArgumentException) {
                        success = false;
                    }
                }      
            }

            if (!success)
            {
                ConsoleUI.WriteToConsole("Bad input. Using default (3,2,2)");
                playerConf.SetStats(3, 2, 2);
            }



            conf.addPlayer(playerConf);

        }


        // TODO: refactoring : remove when no longer needed
        public static void addTestWalls(Board board)
        {
            if ((board.Width > 10) && (board.Height > 10))
            {
                board.GetSquare(3, 2).AddWall(SquareWalls.Left);
                board.GetSquare(4, 2).AddWall(SquareWalls.Left);
                board.GetSquare(5, 2).AddWall(SquareWalls.Left);
                board.GetSquare(6, 2).AddWall(SquareWalls.Left);
                board.GetSquare(7, 2).AddWall(SquareWalls.Left);
                board.GetSquare(3, 2).AddWall(SquareWalls.Down);
                board.GetSquare(3, 3).AddWall(SquareWalls.Down);
                board.GetSquare(3, 4).AddWall(SquareWalls.Down);
                board.GetSquare(3, 4).AddWall(SquareWalls.Right);
                board.GetSquare(4, 2).AddWall(SquareWalls.Up);
                board.GetSquare(4, 3).AddWall(SquareWalls.Up);


                board.GetSquare(4, 8).AddWall(SquareWalls.Left);
                board.GetSquare(5, 8).AddWall(SquareWalls.Left);
                board.GetSquare(6, 8).AddWall(SquareWalls.Left);
                board.GetSquare(7, 8).AddWall(SquareWalls.Left);
                board.GetSquare(8, 8).AddWall(SquareWalls.Left);
                board.GetSquare(4, 10).AddWall(SquareWalls.Right);
                board.GetSquare(5, 10).AddWall(SquareWalls.Right);
                board.GetSquare(6, 10).AddWall(SquareWalls.Left);
                board.GetSquare(7, 10).AddWall(SquareWalls.Right);
                board.GetSquare(8, 10).AddWall(SquareWalls.Right);
                board.GetSquare(3, 8).AddWall(SquareWalls.Up);
                board.GetSquare(3, 9).AddWall(SquareWalls.Up);
                board.GetSquare(3, 10).AddWall(SquareWalls.Up);
                board.GetSquare(6, 10).AddWall(SquareWalls.Up);
                board.GetSquare(6, 10).AddWall(SquareWalls.Down);



                board.GetSquare(5, 14).AddWall(SquareWalls.Left);
                board.GetSquare(6, 14).AddWall(SquareWalls.Left);
                board.GetSquare(7, 14).AddWall(SquareWalls.Left);
                board.GetSquare(8, 14).AddWall(SquareWalls.Left);
                board.GetSquare(9, 14).AddWall(SquareWalls.Left);
                board.GetSquare(5, 17).AddWall(SquareWalls.Left);
                board.GetSquare(6, 17).AddWall(SquareWalls.Left);
                board.GetSquare(7, 17).AddWall(SquareWalls.Left);
                board.GetSquare(8, 17).AddWall(SquareWalls.Left);
                board.GetSquare(9, 17).AddWall(SquareWalls.Left);
                board.GetSquare(5, 14).AddWall(SquareWalls.Down);
                board.GetSquare(5, 15).AddWall(SquareWalls.Down);
                board.GetSquare(5, 16).AddWall(SquareWalls.Down);
                //board.GetSquare(7, 14).AddWall(SquareWalls.Down);
                board.GetSquare(7, 15).AddWall(SquareWalls.Down);
                //board.GetSquare(7, 16).AddWall(SquareWalls.Down);



                board.GetSquare(6, 20).AddWall(SquareWalls.Left);
                board.GetSquare(7, 20).AddWall(SquareWalls.Left);
                board.GetSquare(8, 20).AddWall(SquareWalls.Left);
                board.GetSquare(9, 20).AddWall(SquareWalls.Left);
                board.GetSquare(10, 20).AddWall(SquareWalls.Left);
                board.GetSquare(8, 23).AddWall(SquareWalls.Left);
                board.GetSquare(9, 23).AddWall(SquareWalls.Left);
                board.GetSquare(10, 23).AddWall(SquareWalls.Left);
                board.GetSquare(6, 20).AddWall(SquareWalls.Down);
                board.GetSquare(6, 21).AddWall(SquareWalls.Down);
                board.GetSquare(6, 22).AddWall(SquareWalls.Down);
                board.GetSquare(10, 20).AddWall(SquareWalls.Up);
                board.GetSquare(10, 21).AddWall(SquareWalls.Up);
                board.GetSquare(10, 22).AddWall(SquareWalls.Up);
                board.GetSquare(8, 22).AddWall(SquareWalls.Down);
            }
        }




    }
}
